#pragma once

#include <iostream>
#include <arpa/inet.h>
#include <unordered_map>

#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>
#include <muduo/base/CountDownLatch.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/TcpClient.h>

#include "Tool.hpp"
#include "Fields.hpp"
#include "Abstract.hpp"
#include "Message.hpp"

namespace MyRpc
{
    // 使用 muduo 网络库中的缓冲区来实现
    class MuduoBuffer : public BaseBuffer
    {
    public:
        using muduo_buf_ptr = std::shared_ptr<MuduoBuffer>;
        MuduoBuffer(muduo::net::Buffer *buf) : _buf(buf)
        {
        }

        // 返回当前缓冲区有多少数据
        virtual size_t ReadAbleSize() override
        {
            return _buf->readableBytes();
        }

        // 尝试从缓冲区中获取前4字节: 并不删除
        virtual int32_t TryGetInt32() override
        {
            // muduo 是网络库,因此从 muduo 缓冲区中取出数据时 muduo 会进行 网络字节序->主机字节序 的转换
            // 因此, 后续在往缓冲区写入数据时,需要进行 主机字节序->网络字节序 的转换
            return _buf->peekInt32();
        }

        // 取出缓冲区中删除前4字节:并删除
        virtual void RetrieveInt32() override
        {
            _buf->retrieveInt32();
        }

        // 从缓冲区中获取并删除前4字节,是 Retrieve 和 Try 的合并
        virtual int32_t ReadInt32() override
        {
            return _buf->readInt32();
        }

        // 从缓冲区中取出指定长度数据,并以string形式返回
        virtual std::string RetrieveAsLen(size_t len) override
        {
            return _buf->retrieveAsString(len);
        }

    private:
        // muduo 网络库中的缓冲区
        muduo::net::Buffer *_buf;
    };

    // 生产 MuduoBuffer 的工厂
    class MuduoBufferFactory
    {
    public:
        template <class... Args>
        static BaseBuffer::buf_ptr create(Args &&...args) // 右值引用保证参数属性不变
        {
            return std::make_shared<MuduoBuffer>(std::forward<Args>(args)...); // ...: 将参数展开一一传递
        }
    };

    // 协议格式
    // | --Len--4字节 | --mtype--4字节 | --idLen--4字节 | --id-- | --body-- | : Len表示缓冲区应有的长度,不包含自身长度
    class LVProtocol : public BaseProtocol
    {
    public:
        using lv_pro_ptr = std::shared_ptr<LVProtocol>;
        // 接收数据时的处理

        // 判断缓冲区数据能否处理
        virtual bool CanProcessed(const BaseBuffer::buf_ptr &buf) override
        {
            if(buf->ReadAbleSize() < _total_fields_len)
                return false;

            int32_t len = buf->TryGetInt32();
            if (buf->ReadAbleSize() < (len + _total_fields_len))
                return false;
            return true;
        }

        // 能够处理, 将缓冲区中的数据反序列化为Json返回给msg
        virtual bool ProcessMessage(const BaseBuffer::buf_ptr &buf, BaseMessage::msg_ptr &msg) override
        {
            // | --Len--4字节 | --mtype--4字节 | --idLen--4字节 | --id-- | --body-- | : Len表示缓冲区应有的长度,不包含自身长度

            // 调用 ProcessMessage 时默认数据是完整能处理的
            int32_t total_len = buf->ReadInt32();                                    // 获取数据总长度
            MType mtype = (MType)buf->ReadInt32();                                   // 获取消息类型
            int32_t id_len = buf->ReadInt32();                                       // 获取id长度
            int32_t body_len = total_len - _mtype_fields_len - _id_len_len - id_len; // 获取数据主体长度
            std::string id = buf->RetrieveAsLen(id_len);                             // 获取 id
            std::string body = buf->RetrieveAsLen(body_len);                         // 获取主体消息
            msg = MessageFactory::create(mtype);                                     // 根据消息类型生产消息对象
            if (msg.get() == nullptr)
            {
                LogArea::LOG(LogLevel::ERROR) << "mtype error";
                return false;
            }
            bool ret = msg->UnSerialize(body);
            if (!ret)
            {
                LogArea::LOG(LogLevel::ERROR) << "unserialize error";
                return false;
            }
            msg->SetId(id);
            msg->SetMtype(mtype);
            return true;
        }
        // 发送数据时的处理
        // 将msg序列化为string返回
        virtual std::string Serialize(const BaseMessage::msg_ptr &msg) override
        {
            // | --Len--4字节 | --mtype--4字节 | --idLen--4字节 | --id-- | --body-- | : Len表示缓冲区应有的长度,不包含自身长度

            std::string body = msg->Serialize();
            std::string id = msg->GetId();
            auto mtype = htonl((int32_t)msg->GetMtype());
            int32_t id_len = htonl(id.size());
            int32_t h_total_len = _mtype_fields_len + _id_len_len + id.size() + body.size();
            int32_t n_total_len = htonl(h_total_len);

            std::string result;
            result.reserve(h_total_len); // 提高插入效率

            result.append((char *)&n_total_len, _total_fields_len);
            result.append((char *)&mtype, _mtype_fields_len);
            result.append((char *)&id_len, _id_len_len);
            result.append(id);
            result.append(body);

            return result;
        }

    private:
        const size_t _total_fields_len = 4;
        const size_t _mtype_fields_len = 4;
        const size_t _id_len_len = 4;
    };

    // 生产 LVProtocol 的工厂
    class LVProtocolFactory
    {
    public:
        template <class... Args>
        static BaseProtocol::pro_ptr create(Args &&...args) // 右值引用保证参数属性不变
        {
            return std::make_shared<LVProtocol>(std::forward<Args>(args)...); // ...: 将参数展开一一传递
        }
    };

    class MuduoConnection : public BaseConnection
    {
    public:
        using mu_conn_ptr = std::shared_ptr<MuduoConnection>;
        MuduoConnection(const muduo::net::TcpConnectionPtr &conn, const BaseProtocol::pro_ptr &pro)
            : _pro(pro), _conn(conn)
        {
        }

        // 向客户端发回消息
        virtual void Send(const BaseMessage::msg_ptr &message) override
        {
            std::string body = _pro->Serialize(message);
            _conn->send(body);
        }

        // 断开连接
        virtual void DisConnect() override
        {
            _conn->shutdown();
        }

        // 连接状态
        virtual bool ConnectStatus() override
        {
            return _conn->connected();
        }

    private:
        BaseProtocol::pro_ptr _pro;
        muduo::net::TcpConnectionPtr _conn;
    };

    // 生产 MuduoConnection 的工厂
    class MuduoConnectionFactory
    {
    public:
        template <class... Args>
        static BaseConnection::con_ptr create(Args &&...args) // 右值引用保证参数属性不变
        {
            return std::make_shared<MuduoConnection>(std::forward<Args>(args)...); // ...: 将参数展开一一传递
        }
    };

    // 服务器
    class MuduoServer : public BaseServer
    {
    public:
        using mu_ser_ptr = std::shared_ptr<MuduoServer>;
        MuduoServer(int port)
            : _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), "MuduoServer", muduo::net::TcpServer::kReusePort)
        {
        }

        // 运行服务器
        virtual void Start() override
        {
            _server.setConnectionCallback(std::bind(&MuduoServer::OnConnection, this, std::placeholders::_1));
            _server.setMessageCallback(std::bind(&MuduoServer::OnMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            _server.start();  // 启动监听
            _baseloop.loop(); // 监控事件
        }

    private:
        // 连接时的回调函数
        void OnConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            // 连接建立成功
            if (conn->connected())
            {
                LogArea::LOG(LogLevel::DEBUG) << "Connection Success";
                auto muduo_conn = MuduoConnectionFactory::create(conn, _protocol);
                {
                    MyMutexArea::LockGuard lock(_mutex);
                    _conns.insert(std::make_pair(conn, muduo_conn));
                }
                // 如果用户设置了连接回调函数,则运行
                if (_server_conn_cb)
                    _server_conn_cb(muduo_conn);
            }
            else
            {
                LogArea::LOG(LogLevel::DEBUG) << "Connection Disconnect";
                BaseConnection::con_ptr muduo_conn;
                {
                    MyMutexArea::LockGuard lock(_mutex);
                    auto it = _conns.find(conn);
                    if (it == _conns.end())
                        return;
                    muduo_conn = it->second;
                    _conns.erase(conn);
                }

                // 如果用户设置了断开连接的回调函数,则运行
                if (_server_close_cb)
                    _server_close_cb(muduo_conn);
            }
        }

        // 收到消息后的回调函数
        void OnMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp)
        {
            // 构建一个muduo缓冲区对象
            auto base_buf = MuduoBufferFactory::create(buf);

            while (1)
            {
                // 数据不完成,无法进行处理
                if (_protocol->CanProcessed(base_buf) == false)
                {
                    // 缓冲区数据超过限定大小
                    if (base_buf->ReadAbleSize() > _max_data_size)
                    {
                        conn->shutdown();
                        LogArea::LOG(LogLevel::ERROR) << "Buffer Data TooLong";
                        return;
                    }
                    break;
                }
                BaseMessage::msg_ptr msg;
                bool ret = _protocol->ProcessMessage(base_buf, msg);
                if (!ret)
                {
                    conn->shutdown();
                    LogArea::LOG(LogLevel::ERROR) << "Buffer Data Error";
                    return;
                }

                // 获取 客户端->服务器 的连接
                BaseConnection::con_ptr base_conn;
                {
                    MyMutexArea::LockGuard lock(_mutex);
                    auto it = _conns.find(conn);
                    // 没有建立连接
                    if (it == _conns.end())
                    {
                        conn->shutdown();
                        return;
                    }
                    base_conn = it->second;
                }

                // 如果用户设置了消息回调函数,则运行
                if (_server_msg_cb)
                    _server_msg_cb(base_conn, msg); // 服务端有多个连接
            }
        }

    private:
        const size_t _max_data_size = (1 << 16);
        BaseProtocol::pro_ptr _protocol;
        muduo::net::EventLoop _baseloop;
        muduo::net::TcpServer _server;
        MyMutexArea::MyMutex _mutex;
        std::unordered_map<muduo::net::TcpConnectionPtr, BaseConnection::con_ptr> _conns; // 客户端与服务器连接的映射
    };

    // 生产 MuduoServer 的工厂
    class MuduoServerFactory
    {
    public:
        template <class... Args>
        static BaseServer::server_ptr create(Args &&...args) // 右值引用保证参数属性不变
        {
            return std::make_shared<MuduoServer>(std::forward<Args>(args)...); // ...: 将参数展开一一传递
        }
    };

    // 客户端
    class MuduoClient : public BaseClient
    {
    public:
        using mu_cli_ptr = std::shared_ptr<MuduoClient>;

        MuduoClient(const std::string &sip, int port)
            : _protocol(LVProtocolFactory::create()), 
            _base_loop(_loop_thread.startLoop()), 
            _down_latch(1), 
            _client(_base_loop, muduo::net::InetAddress(sip, port), "MuduoClient")
        {
        }

        // 发起连接
        virtual void Connect() override
        {
            _client.setConnectionCallback(std::bind(&MuduoClient::OnConnection, this, std::placeholders::_1));
            _client.setMessageCallback(std::bind(&MuduoClient::OnMessage, this,
                                                 std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));

            // 连接服务器
            _client.connect();
            _down_latch.wait(); // 等待计数器减为0,也就是等待所有连接建立成功,然后唤醒阻塞
            LogArea::LOG(LogLevel::DEBUG)<<"Connected Server Success";
        }

        // 断开连接
        virtual void DisConnect() override
        {
            _client.disconnect();
        }

        // 发送消息
        virtual bool Send(const BaseMessage::msg_ptr &msg) override
        {
            if (ConnectStatus() == false)
            {
                LogArea::LOG(LogLevel::DEBUG) << "Disconnected";
                return false;
            }
            _conn->Send(msg);
            return true;
        }

        // 获取客户端连接对象
        virtual BaseConnection::con_ptr GetConnect() override
        {
            return _conn;
        }

        // 连接状态
        virtual bool ConnectStatus()
        {
            return (_conn && _conn->ConnectStatus());
        }

    private:
        void OnConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            // 连接建立成功
            if (conn->connected())
            {
                LogArea::LOG(LogLevel::DEBUG) << "Connection Success";
                _down_latch.countDown(); // 计数器--,为0时唤醒阻塞的线程

                _conn = MuduoConnectionFactory::create(conn, _protocol);
            }
            else
            {
                // 连接断开,客户端释放当前连接
                LogArea::LOG(LogLevel::DEBUG) << "Disconnection";
                _conn.reset();
            }
        }

        // 获取消息,进行处理
        void OnMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp)
        {
            // 构建一个muduo缓冲区对象
            auto base_buf = MuduoBufferFactory::create(buf);

            while (1)
            {
                // 数据不完成,无法进行处理
                if (_protocol->CanProcessed(base_buf) == false)
                {
                    // 缓冲区数据超过限定大小
                    if (base_buf->ReadAbleSize() > _max_data_size)
                    {
                        conn->shutdown();
                        LogArea::LOG(LogLevel::ERROR) << "Buffer Data TooLong";
                        return;
                    }
                    break;
                }
                BaseMessage::msg_ptr msg;
                bool ret = _protocol->ProcessMessage(base_buf, msg);
                if (!ret)
                {
                    conn->shutdown();
                    LogArea::LOG(LogLevel::ERROR) << "Buffer Data Error";
                    return;
                }

                // 如果用户设置了消息回调函数,则运行
                if (_client_msg_cb)
                    _client_msg_cb(_conn, msg); // 客户端只有一个连接
            }
        }

    private:
        const size_t _max_data_size = (1 << 16);
        BaseProtocol::pro_ptr _protocol;
        BaseConnection::con_ptr _conn;
        muduo::CountDownLatch _down_latch;        // 需要等待的正在执行连接的线程的数量: 每有一个线程完成任务,downlatch--,此时可以再唤醒一个线程
        muduo::net::EventLoopThread _loop_thread; // 负责创建和管理一个独立的线程,用于监听
        muduo::net::EventLoop *_base_loop;
        muduo::net::TcpClient _client;
    };

    // 生产 MuduoServer 的工厂
    class MuduoClientFactory
    {
    public:
        template <class... Args>
        static BaseClient::client_ptr create(Args &&...args) // 右值引用保证参数属性不变
        {
            return std::make_shared<MuduoClient>(std::forward<Args>(args)...); // ...: 将参数展开一一传递
        }
    };
}
